package com.wennn.top.security.util;

import com.wennn.top.security.model.JwtAuthToken;
import com.wennn.top.security.model.token.JwtRawAccessToken;
import com.wennn.top.util.JsonUtil;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.impl.DefaultHeader;
import io.jsonwebtoken.impl.DefaultJwsHeader;
import io.jsonwebtoken.impl.TextCodec;
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 *
 */
public class JwtUtil {

    private static CompressionCodecResolver codecResolver = new DefaultCompressionCodecResolver();


    public static String praseHeaderToken(String unsaftToken){
        Assert.notNull(unsaftToken,"Token");
        return unsaftToken.replace(SecurityConst.AUTH_HEADER_PRE, "");
    }

    public static String tokenToHeader(String token){
        Assert.notNull(token,"Token");
        return SecurityConst.AUTH_HEADER_PRE + token;
    }


    /**
     * @description: 通过request 获取token
     * @param: request
     * @return: 解析后的token
     * @author: wennn
     * @date: 5/9/20 3:58 PM
     */
    public static JwtAuthToken getAuthentication(HttpServletRequest request) {
        String token = getRequestToken(request);
        return token ==null?null: new JwtAuthToken(new JwtRawAccessToken(JwtUtil.praseHeaderToken(token)));
    }

    public static String getRequestToken(HttpServletRequest request){
        String token = request.getHeader(SecurityConst.AUTH_HEADER);
        if (token == null || token.isEmpty() || JwtUtil.praseHeaderToken(token).isEmpty()) {
            return null;
        }
        return token;
    }

    /**
     * 解密 tokenStr字符串
     * @param tokenStr
     * @return
     */
    public static Map parseJwtPayload(String tokenStr) {
        Assert.hasText(tokenStr, "JWT String argument cannot be null or empty.");
        String base64UrlEncodedHeader = null;
        String base64UrlEncodedPayload = null;
        String base64UrlEncodedDigest = null;
        int delimiterCount = 0;
        StringBuilder sb = new StringBuilder(128);
        for (char c : tokenStr.toCharArray()) {
            if (c == '.') {
                CharSequence tokenSeq = io.jsonwebtoken.lang.Strings.clean(sb);
                String token = tokenSeq != null ? tokenSeq.toString() : null;

                if (delimiterCount == 0) {
                    base64UrlEncodedHeader = token;
                } else if (delimiterCount == 1) {
                    base64UrlEncodedPayload = token;
                }

                delimiterCount++;
                sb.setLength(0);
            } else {
                sb.append(c);
            }
        }
        if (delimiterCount != 2) {
            String msg = "JWT strings must contain exactly 2 period characters. Found: " + delimiterCount;
            throw new MalformedJwtException(msg);
        }
        if (sb.length() > 0) {
            base64UrlEncodedDigest = sb.toString();
        }
        if (base64UrlEncodedPayload == null) {
            throw new MalformedJwtException("JWT string '" + tokenStr + "' is missing a body/payload.");
        }
        // =============== Header =================
        Header header = null;
        CompressionCodec compressionCodec = null;
        if (base64UrlEncodedHeader != null) {
            String origValue = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader);
            Map<String, Object> m = readValue(origValue);
            if (base64UrlEncodedDigest != null) {
                header = new DefaultJwsHeader(m);
            } else {
                header = new DefaultHeader(m);
            }
            compressionCodec = codecResolver.resolveCompressionCodec(header);
        }
        // =============== Body =================
        String payload;
        if (compressionCodec != null) {
            byte[] decompressed = compressionCodec.decompress(TextCodec.BASE64URL.decode(base64UrlEncodedPayload));
            payload = new String(decompressed, io.jsonwebtoken.lang.Strings.UTF_8);
        } else {
            payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload);
        }
        Map<String, Object> map = JsonUtil.toObj(payload, HashMap.class);
        return map;
    }

    /**
     * @Description
     * @Param [val] 从json数据中读取格式化map
     * @Return java.util.Map<java.lang.String,java.lang.Object>
     */
    public static Map<String, Object> readValue(String val) {
        try {
            return JsonUtil.toObj(val,HashMap.class);
        } catch (Exception e) {
            throw new MalformedJwtException("Unable to read JSON value: " + val, e);
        }
    }
}
